include(['com.unknown.Constants', 'com.unknown.Config', 'CanvasWrapper', 'constants.Screen'],
		
function(){
	
	declare('com.unknown.GameWindow');
	declare('com.unknown.Entity');
	
	com.unknown.GameWindow = function(canvas, imageBucket, player){
		this.canvas = canvas;
		//this.c = canvas.getContext('2d');
		this.c = new CanvasWrapper(canvas.getContext('2d'));
		
		this.loadedimages = [];
		this.imageBucket = $(imageBucket);
		
		this.backgrounds = [];
		this.billboards = [];
	
		this.renderers = [];
		
		this.entities = [];
		this.totalFrames = 0;
		this.skip = 0;

		this.requestAnimationFrame = window.mozRequestAnimationFrame || window.webkitRequestAnimationFrame;
		this.requestAnimationFrame = this.requestAnimationFrame || setTimeout

		var __scope = this;
        var framerate = null;

		window.onblur = function(){
			//__scope.paused = true;
			//$('#spinner').show();
		}

		if( player ){
			this.player = player;
				
			this.entities[this.player.name] = this.player;
		}
				
		this.start = function(){
			var gw = this;
            $(function(){
                gw.framerate = document.getElementById('framerate');
            });

			this.requestAnimationFrame.apply(window, [function(){
				gw.mainLoop();
			}, 0]);
		};
		
		this.addEntity = function(name){
			var e = new com.unknown.Entity( this.player.img );
			e.name = name;
			this.entities[name] = e;		
		};
		
		this.orientationChanged = function(){
			this.c.scale(1, 1);
		};
	
		this.createImage = function(src){
			if(!this.loadedimages[src]){
				var img = document.createElement('img');
				$(img).attr('src', src);
				this.imageBucket.append(img);
				this.loadedimages[src] = {img: img, count: 1};
			}else{
				this.loadedimages[src].count++;
			}
			return this.loadedimages[src];
		};

        this.timestamp = 0;
        this.lastUpdated = 0;
        this.lastFramerate = 0;
		this.mainLoop = function(){
			if( this.paused ) return;
			//this.canvas.width = this.canvas.width			
			//this.c.clearRect( 0, 0, this.canvas.width, this.canvas.height);
			
			this.totalFrames++;

            this.timestamp = new Date().getTime();
            //35 updates a second = 1000/35 ~= 29
            if( this.timestamp - this.lastUpdated > 29 ){
                this.mainUpdate();
                this.lastUpdated = this.timestamp;
            }
            if( this.framerate && (this.timestamp - this.lastFramerate > 5000) ){
                this.framerate.innerHTML = 'Frames: ' + (__scope.totalFrames/5) + 'fps';
                this.totalFrames = 0;
                this.lastFramerate = this.timestamp;
            }

			//this.clearObjects();
			this.c.clear();
			
			this.render();
			
			var gw = this;
			this.requestAnimationFrame.apply(window,[function(){
				gw.mainLoop();
			}, 0]);
		};
		this.mainUpdate = function(){
			var gw = this;
			iterate(this.entities, function(item){
				if( item.update ){
					item.update(gw);
				}
			});
		};	
		this.clearObjects = function(){
			var c = this.c;
			iterate(this.entities, function(entity){
				if( entity.clearRect ){
					var rect = entity.clearRect();
					c.clearRect(rect.x, rect.y, rect.width, rect.height);
				}
			});
		};		
		this.render = function(){
			for(var e in this.renderers ){
				var renderer = this.renderers[e];
				if(renderer && !(typeof(renderer) == 'function' )){
					var pass = false;
					try{
						this.c.save();
						renderer.render(this);
						pass = true;
					}catch(e){
						console.error(e);
					}finally{
						if( !pass ){
							console.error("Removing renderer: " + renderer.name);
							this.renderers[e] = null;
						}
						this.c.restore();
					}				
				}
			}
			var c = this.c;
			iterate(this.billboards, function(billboard){
				billboard.draw(c);
			});
			
		};	
		
		this.normalize = function(vec2){
			if( vec2.x > this.canvas.width - 60 ) 
				vec2.x = this.canvas.width - 60;
			if( vec2.x < 15 ) 
				vec2.x = 15;
			
			if( vec2.y > this.canvas.height - 60 ) 
				vec2.y = this.canvas.height - 60;
			if( vec2.y < 15 ) 
				vec2.y = 15;
			
			return vec2;
		};
		
		this.draw = function(src, x, y){
			var img = this.loadedimages[src];
			if(img.img.width == 0 ) return;
			
			if( arguments.length == 9 ){
				this.c.drawImage(img.img, arguments[1], arguments[2], arguments[3], arguments[4], arguments[5], arguments[6], arguments[7], arguments[8]);
			}else{
				this.c.drawImage(img.img, x, y);
			}
		};
		
		this.moveBackground = function(x, y){
			for(var index=0; index < this.backgrounds.length; index++){
				this.backgrounds[index].move(x,y);
			}
		};
		
		if( this.player ){
			this.createImage( this.player.img );
		}

        this.containsPoint = function(point){
            return point.x > 0 &&
            point.y > 0 &&
            point.x < (this.canvas.width - constants.Screen.TOTAL_MARGIN_WIDTH) &&
            point.y < (this.canvas.height - constants.Screen.TOTAL_MARGIN_HEIGHT);
        };
	}
	
	var STILL = 0;
	var LEFT  = 1;
	var RIGHT = 2;
	var UP    = 4;
	var DOWN  = 8;
	
	var MAX_SPEED = 2;
	
	var FASTEST = 15;
	var NORMAL = 10;
	var SLOW = 5;
	var NOT_MOVING = 0;
	
	com.unknown.Entity = function(img){
		this.name = 'Player ' + Math.random();
		this.img = img;
		
		this.x = 16;
		this.y = 16;
		
		this.oldX = 16;
		this.oldY = 16;
		
		this.width = 50;
		this.height = 50;
		
		this.tmp = new Vector2(0,0);
		this.tmp2 = new Vector2(0,0);
		
		this.imageSeq = 0;
		this.imageIndex = 14;
	
		this._input  = new Vector2(0,0);
		this.cell = null;

		this.inputChanged = function(input){
			input.x = input.x - (input.x % 3)
			input.y = input.y - (input.y % 3)
			
			if( this._input.x != input.x || this._input.y != input.y){
				
				this._input.reset(input.x, input.y);

                this.sendPositionUpdate();
			}
		};

        this.lastNetworkUpdate = 0;
        this.sendPositionUpdate = function(){
            if( !this.cell ) return;

            var position = this.cell.toLocalCoordinates(this.x, this.y);

            var update = {
                id : this.name
                ,vector : this._input
                ,roomId : this.cell.owner.roomId
                ,roomX : position.x
                ,roomY : position.y
            };

            bus.inputChanged(update);
        };
	
		this.toFuzzy = function(numbers, vector, store){
			if(!store){
				store = new Vector2(0,0);
			}		
			store.x = this._numberToFuzzy(numbers, vector.x);
			store.y = this._numberToFuzzy(numbers, vector.y);
			return store;
		};
		this._numberToFuzzy = function(numbers, number){
			var abs = Math.abs(number);
			var sign = number < 0 ? -1 : 1;
			for(var index=0; index < numbers.length; index++){
				if( abs <= numbers[index] ) return numbers[index] * sign;
			}
			return numbers[numbers.length-1] * sign;
		};
		
		this.determineDirection = function(){
			var dir = STILL;
			if( this._input.x > 5 ) dir = dir | RIGHT; 
			if( this._input.x < -5 ) dir = dir | LEFT;
			if( this._input.y > 0 ) dir = dir | DOWN;
			if( this._input.y < 0 ) dir = dir | UP;
			return dir;
		};
		
		this.update = function(gamewindow){
			var vec = this._input;
			this.imageIndex--;
			if( this.imageIndex < 0 ){
				this.imageIndex = 14;
			}
			
			var moving = vec.x != 0 || vec.y != 0;
			if( !moving ){
				this.imageIndex = 16;
			}
			
			var dir = this.determineDirection();
			var previousSeq = this.imageSeq;
			if( !dir ){
				//DO Nothing
			}
			else if( (dir ^ (LEFT | UP)) == 0 )  
				this.imageSeq = 2;
			else if( (dir ^ (LEFT | DOWN)) == 0 || (dir ^ (LEFT)) == 0 ) 
				this.imageSeq = 0;
			else if( (dir ^ (RIGHT | UP)) == 0 )  
				this.imageSeq = 3;
			else if( (dir ^ (RIGHT | DOWN)) == 0 || (dir ^ (RIGHT)) == 0 ) 
				this.imageSeq = 4;
			else if( (dir ^ (DOWN)) == 0) 
				this.imageSeq = 1;
			else if( (dir ^ (UP)) == 0) 
				this.imageSeq = 5;
			else
				this.imageSeq = 1;
			
			this.x += Math.floor(Math.min(Math.abs(vec.x) * 0.2, MAX_SPEED) * (vec.x > 0 ? 1 : -1));
			this.y += Math.floor(Math.min(Math.abs(vec.y) * 0.2, MAX_SPEED) * (vec.y > 0 ? 1 : -1));

            if(moving && gamewindow.timestamp - this.lastNetworkUpdate > 2000){
                this.sendPositionUpdate();
                this.lastNetworkUpdate = gamewindow.timestamp;
            }
		};
		
		this.clearRect = function(){
			return {x:this.x, y:this.y-10, width:70, height:60}; 
		};
		
		this.render = function(gamewindow){
			this.tmp.reset(this.x, this.y)
			var loc = gamewindow.normalize(this.tmp);
	
			var deltaX = 0;
			var deltaY = 0;
						
			var skip = this.skip;
			
			if( loc.x < this.x ){
				deltaX = (this.x - loc.x)+(skip || 0);
				this.x = loc.x - (skip || 0);
			}
			if( loc.y < this.y ){
				deltaY = (this.y - loc.y)+(skip || 0);
				this.y = loc.y - (skip || 0);
			}
			if( this.x < 20 ){
				deltaX = this.x-20+(skip || 0);
				this.x = 20 + (skip || 0);
			}
			if( this.y < 20 ){
				deltaY = (this.y)-20+(skip || 0);
				this.y = 20 + (skip || 0);
			}
			
			this.drawEntity(deltaX, deltaY, gamewindow);
		};
		this.drawEntity = function(deltaX, deltaY, gw){
            //I've got to clean this up and some point and move all of these
            //constants to an appropriate constant data structure (or data structures)
			var x = this.x;
			var y = Math.floor(this.y+this.height-10);
			
			var oldX = this.oldX;
			var oldY = this.oldY;
			
			var tile = room.getTile(x+25,y);
			if( tile ){
                this.cell = tile;

				gamewindow.c.font = '10px sans-serif';
				gamewindow.c.fillText(this.name, this.x+40, this.y);
				gamewindow.draw(this.img, (this.imageIndex + (this.imageSeq*17)) * 100, 0, 100, 100, this.x, this.y, 50, 50);
				var exit = tile.roomid ? tile : null;
				if( exit ){
					gamewindow.c.font = '20px sans-serif';
					gamewindow.c.fillText(exit.name, this.x+70, this.y+40);
				}
				if( (deltaX || deltaY) && this == gw.player ){
					eGrid.translate(-deltaX, -deltaY);
				}
			}else{
				this.x = oldX;
				this.y = oldY;
				gamewindow.draw(this.img, (this.imageIndex + (this.imageSeq*17)) * 100, 0, 100, 100, this.x, this.y, 50, 50);
			}
			this.oldX = this.x;
			this.oldY = this.y;
		};
		this.activate = function(){
			var exit = room.getTile(this.x+25,Math.floor(this.y+this.height-10));
			if( exit.roomid ){
				room = new com.unknown.Room(roomUrl, exit.roomid, 'show');
				
				eGrid.reload();
			}
		};
	};
});